/*
 * This code was written by Bear Giles <bgiles@coyotesong.com>and he
 * licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the
 * License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Any contributions made by others are licensed to this project under
 * one or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 * Copyright (c) 2012 Bear Giles <bgiles@coyotesong.com>
 */
package com.invariantproperties.crypto.pljava;

import java.security.Provider;
import java.sql.ResultSet;
import java.sql.SQLException;

// import java.util.ArrayList;
// import java.util.List;

import org.postgresql.pljava.ResultSetProvider;

/**
 * Class that makes static methods of java.security.Security class available to
 * postgresql users.
 * 
 * IMPORTANT RESTRICTION: The pljava implementation of java.security.Provider is
 * incomplete, specifically the class java.security.Provider.Service is missing
 * and any reference to it will cause a confusing 'MethodNotFoundException' on
 * some or all methods in this class.
 * 
 * 
 * @author bgiles@coyotesong.com
 */
public class Security {

    /**
     * Initialize the class. We could load additional providers here.
     */
    public static void init() {
        // Security.addProvider(new
        // org.bouncycastle.jce.provider.BouncyCastleProvider());
    }

    /**
     * Get a list of security providers.
     * 
     * @return
     */
    public static ResultSetProvider listAllProviders() {
        return new ProviderResultSetProvider(
                java.security.Security.getProviders());
    }

    /**
     * Get information about a specific provider.
     * 
     * @param provider
     * @return
     */
    public static ResultSetProvider listProviders(String provider) {
        Provider p = java.security.Security.getProvider(provider);
        if (p == null) {
            return null;
        }
        return new ProviderResultSetProvider(new Provider[] { p });
    }

    /**
     * Get a list of services.
     * 
     * @return
     */
    public static ResultSetProvider listAllServices() throws SQLException {
        throw new SQLException(
                "pljava does not currently support java.security.Provider.Service class");

        // List<Provider.Service> services = new ArrayList<Provider.Service>();

        // for (Provider provider : java.security.Security.getProviders()) {
        // services.addAll(provider.getServices());
        // }

        // return new ServiceResultSetProvider(
        // services.toArray(new Provider.Service[0]));
    }

    /**
     * Get a list of services from a specific provider.
     * 
     * @param provider
     */
    public static ResultSetProvider listServices(String provider)
            throws SQLException {
        Provider p = java.security.Security.getProvider(provider);
        if (p == null) {
            return null;
        }

        throw new SQLException(
                "pljava does not currently support java.security.Provider.Service class");

        // return new ServiceResultSetProvider(p.getServices().toArray(
        // new Provider.Service[0]));
    }

    /**
     * ResultSetProvider for provider information. This creates a record with
     * three columns:
     * 
     * (provider varchar, version float8, info varchar)
     * 
     * @author bgiles@coyotesong.com
     */
    public static class ProviderResultSetProvider implements ResultSetProvider {
        private Provider[] providers;

        public ProviderResultSetProvider(Provider[] providers) {
            // make defensive copy
            this.providers = providers.clone();
        }

        /**
         * @see org.postgresql.pljava.ResultSetProvider#assignRowValues(java.sql.ResultSet,
         *      int)
         */
        @Override
        public boolean assignRowValues(ResultSet receiver, int currentRow)
                throws SQLException {
            if (providers == null || currentRow < 0
                    || currentRow >= providers.length) {
                return false;
            }

            receiver.updateString("provider", providers[currentRow].getName());
            receiver.updateDouble("version", providers[currentRow].getVersion());
            receiver.updateString("info", providers[currentRow].getInfo());

            return true;
        }

        /**
         * @see org.postgresql.pljava.ResultSetProvider#close()
         */
        @Override
        public void close() throws SQLException {
            providers = null;
        }
    }

    /**
     * ResultSetProvider for service information. This creates a record with
     * three columns:
     * 
     * (provider varchar, service varchar, algorithm varchar)
     * 
     * IMPORTANT: the current implementation does not include
     * java.security.Provider.Service. This class is still fine since it will
     * still compile and the missing class isn't a problem unless we attempt to
     * instantiate this class.
     * 
     * @author bgiles@coyotesong.com
     */
    public static class ServiceResultSetProvider implements ResultSetProvider {
        private Provider.Service[] services;

        public ServiceResultSetProvider(Provider.Service[] services) {
            // make defensive copy
            this.services = services.clone();
        }

        /**
         * @see org.postgresql.pljava.ResultSetProvider#assignRowValues(java.sql.ResultSet,
         *      int)
         */
        @Override
        public boolean assignRowValues(ResultSet receiver, int currentRow)
                throws SQLException {
            if (services == null || currentRow < 0
                    || currentRow >= services.length) {
                return false;
            }

            receiver.updateString("provider", services[currentRow]
                    .getProvider().getName());
            receiver.updateString("service", services[currentRow].getType());
            receiver.updateString("algorithm",
                    services[currentRow].getAlgorithm());

            return true;
        }

        /**
         * @see org.postgresql.pljava.ResultSetProvider#close()
         */
        @Override
        public void close() throws SQLException {
            services = null;
        }
    }
}